/*
 *  OlympicWorkouts.cpp
 *  Workout
 *
 *  Created by Matt Krugman on 12/22/10.
 *  Copyright 2010 Acroname Robotics. All rights reserved.
 *
 */

#include "OlympicWorkouts.h"
#include "Exercise.h"

int gCombo = 0;
bool gbSquat = true;

////////////////////////////////////////////////////////////////////////////////
// Function callback for creating a olympic based routine
// This workout only has 3 days of workout per week

void OlympicTrainingProc(int *pDate, 
														const int week, 
														int *pBasePercent, 
														int *pDeltaPercent, 
														int *pDayIncrements, 
														int incrementLength, 
														void* vpRef)
{
	int day = 0;
	char des[30];
	
	// Create a working copy to the workoutprogram pointer
  WorkoutProgram* pW = (WorkoutProgram*) vpRef;
	
	// Name the workout
	// only necessary on the first pass
	if (week == 0)
		pW->SetWorkoutProgramDescription("Olympic Training");
	
	// Get the current workout index
	int index = pW->GetWorkoutLastIndex();
	
	// Calculate the next percentage base on our percent calculation function
	float percent = pW->m_percentProc(week, 
																		pW->GetWorkoutProgramNumWeeks(), 
																		pBasePercent, 
																		pW->GetWorkoutProgramNumCycles(), 
																		pDeltaPercent);
	
	/////////////////////////////////////////////////////////////////////
	// Light workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","L", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + (float)day*0.05f);
	
  // Add the workout
	
	pW->AddCombination(&pW->m_workouts[index], &gCombo, set3x1, false, calcSine);
	pW->RandomJerk(&pW->m_workouts[index], set5x5);
	pW->AlternateSquat(&pW->m_workouts[index], &gbSquat, set5x8, false, calcLinear, 0.8f);
	pW->RandomPull(&pW->m_workouts[index], set5x5);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[0]);
	
	/////////////////////////////////////////////////////////////////////
	// Medium workout day
	// This is 10 percent harder than the first day of the week
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","M", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + 0.1f);
	
  // Add the workout
	pW->m_workouts[index].AddExercise(Snatch, 
																		set3x3, 
																		pW->ExerciseGoalGet(Snatch),
																		0, 
																		calcSine,
																		filterOlympic,
																		false);
	
	pW->RandomClean(&pW->m_workouts[index], set2x5);
	
	pW->m_workouts[index].AddExercise(JerkRack, 
																		set5x5, 
																		pW->ExerciseGoalGet(JerkRack),
																		0, 
																		calcSine,
																		filterOlympic,
																		false);
	
	pW->RandomPress(&pW->m_workouts[index], set5x5, true);
	pW->AlternateSquat(&pW->m_workouts[index], &gbSquat, set5x5, false, calcLinear, 0.8f);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[1]);
	
	/////////////////////////////////////////////////////////////////////
	// Heavy workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","H", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + 0.2f);
	
  // Add the workout
	pW->m_workouts[index].AddExercise(Snatch, 
																		set3x1, 
																		pW->ExerciseGoalGet(Snatch),
																		0, 
																		calcSine,
																		filterOlympic,
																		false);
	
	pW->m_workouts[index].AddExercise(CleanAndJerk, 
																		set3x1, 
																		pW->ExerciseGoalGet(CleanAndJerk),
																		0, 
																		calcSine,
																		filterOlympic,
																		false);
	
	pW->AlternateSquat(&pW->m_workouts[index], &gbSquat, set3x3, false, calcLinear, 0.9f);
	pW->RandomPull(&pW->m_workouts[index], set5x3);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[2]);
	
} // OlympicTraiingProc

////////////////////////////////////////////////////////////////////////////////
// Function callback for creating a olympic based routine
// This workout only has 3 days of workout per week

void OlympicMaintenanceProc(int *pDate, 
														const int week, 
														int *pBasePercent, 
														int *pDeltaPercent, 
														int *pDayIncrements, 
														int incrementLength, 
														void* vpRef)
{
	int day = 0;
	char des[30];
	
	// Create a working copy to the workoutprogram pointer
  WorkoutProgram* pW = (WorkoutProgram*) vpRef;
	
	// Name the workout
	// only necessary on the first pass
	if (week == 0)
		pW->SetWorkoutProgramDescription("Olympic Maintenance");
	
	// Get the current workout index
	int index = pW->GetWorkoutLastIndex();
	
	// Calculate the next percentage base on our percent calculation function
	float percent = pW->m_percentProc(week, 
																		pW->GetWorkoutProgramNumWeeks(), 
																		pBasePercent, 
																		pW->GetWorkoutProgramNumCycles(), 
																		pDeltaPercent);
	
	/////////////////////////////////////////////////////////////////////
	// Light workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","L", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + (float)day*0.05f);
	
  // Add the workout
	pW->m_workouts[index].AddExercise(Snatch, 
																		set5x5, 
																		pW->ExerciseGoalGet(Snatch),
																		0, 
																		calcSine,
																		filterOlympic,
																		false);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[0]);
	
	/////////////////////////////////////////////////////////////////////
	// Medium workout day
	// This is 10 percent harder than the first day of the week
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","M", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + 0.1f);
	
  // Add the workout
	pW->m_workouts[index].AddExercise(CleanAndJerk, 
																		set3x3, 
																		pW->ExerciseGoalGet(CleanAndJerk),
																		0, 
																		calcSine,
																		filterNothing,
																		false);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[1]);
	
	/////////////////////////////////////////////////////////////////////
	// Heavy workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Maintenance - %s (Week %d)","H", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent + 0.2f);
	
  // Add the workout
	pW->m_workouts[index].AddExercise(Press, 
																		set3x1, 
																		pW->ExerciseGoalGet(Press),
																		0, 
																		calcSine,
																		filterNothing,
																		false);
	
	int combo = 0;
	pW->AddCombination(&pW->m_workouts[index], &combo, set5x5);
	pW->RandomSnatch(&pW->m_workouts[index], set3x1, false, calcSine);
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[2]);
	
} // OlympicMaintenanceProc

float calcUnitStepFactor(float desiredPercent);

float calcUnitStepFactor(float desiredPercent) {
	
	float error = 1.0e9f;
	float k = 0.0f;
	float tolerance = 0.01f;
	int numSteps = 1;
	int totalSteps = 5;
	int finishWeight = 100;
	float basePercent = 1.0f;
	float slopBasePercent = k;
	float percent;
	
	printf("#################################################################\n");

	
	do {
		
		slopBasePercent += 0.01f;
		
		calcUnitStep(numSteps, 
								 totalSteps, 
								 finishWeight, 
								 basePercent, slopBasePercent, &percent);
		
		
		error = percent - desiredPercent;
		error = (error < 0.0f) ? -1*error : error;
		
	} while (error > tolerance);
	
	printf("#################################################################\n");

	return slopBasePercent;
}

////////////////////////////////////////////////////////////////////////////////
// Function callback for creating a olympic based routine
// This workout only has 3 days of workout per week

void OlympicContest900Proc(int *pDate, 
													 const int week, 
													 int *pBasePercent, 
													 int *pDeltaPercent, 
													 int *pDayIncrements, 
													 int incrementLength, 
													 void* vpRef)
{
	char des[30];
	int reps[10];
	
	for (int n = 0; n < 10; n++) {
		reps[n] = 0;
	}
	
	// Create a working copy to the workoutprogram pointer
  WorkoutProgram* pW = (WorkoutProgram*) vpRef;
	
	// Name the workout
	// only necessary on the first pass
	if (week == 0)
		pW->SetWorkoutProgramDescription("Olympic Contest 900");
	
	// Get the current workout index
	int index = pW->GetWorkoutLastIndex();
	
	// Calculate the next percentage base on our percent calculation function
	float percent = pW->m_percentProc(week, 
																		pW->GetWorkoutProgramNumWeeks(), 
																		pBasePercent, 
																		pW->GetWorkoutProgramNumCycles(), 
																		pDeltaPercent);
	// we have specific values to adhere to.
	// thus.
	percent = 1.0f;
	
	/////////////////////////////////////////////////////////////////////
	// 1st workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Contest - Day 1 (Week %d)", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	
	switch (week) {
		case 0: // 1st week, 1st day
			
			// Add the exercises to the workout
			pW->m_workouts[index].AddExercise(CombinationH, 
																				setTaper6x332222, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.6f, 1.0f),
																				0, 
																				calcUnitStep,
																				filterNothing,
																				false, 0.5f);
			
			pW->m_workouts[index].AddExercise(JerkRack, 
																				set4x3, 
																				pW->ExerciseGoalGetAdjustment(JerkRack, 0.75f, 1.0f),
																				0, 
																				calcUnitStep,
																				filterNothing,
																				false, 0.9f);
			
			pW->m_workouts[index].AddExercise(BackSquat, 
																				set6x5, 
																				pW->ExerciseGoalGetAdjustment(BackSquat, 0.75f, 1.0f),
																				0, 
																				calcUnitStep,
																				filterNothing,
																				false, 0.9f);
			
			break;
			
		case 1: // 2nd week, 1st day
			
			// snatch exercise
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 4;
			reps[3] = 4;
			reps[4] = 3;
			reps[5] = 3;
			reps[6] = 3;
			pW->m_workouts[index].AddExercise(SnatchPower, 
																				7, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.6f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.6f);
			
			// clean and jerk exercise
			reps[0] = 2;
			reps[1] = 2;
			reps[2] = 2;
			reps[3] = 2;
			reps[4] = 2;
			reps[5] = 1;
			reps[6] = 1;
			pW->m_workouts[index].AddExercise(CleanAndJerk, 
																				7, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.8f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.6f);
			
			pW->m_workouts[index].AddExercise(BackSquat, 
																				set7x4, 
																				pW->ExerciseGoalGetAdjustment(BackSquat, 0.8f, 1.0f),
																				0, 
																				calcUnitStep,
																				filterNothing,
																				false, 0.9f);
			
			
			// overhead squat exercise
			pW->m_workouts[index].AddExercise(OverheadSquat, 
																				set5x3, 
																				pW->ExerciseGoalGetAdjustment(OverheadSquat, 0.75f, 1.0f),
																				0, 
																				calcSine,
																				filterNothing,
																				false, 0.85f);
			
			// overhead squat exercise
			pW->m_workouts[index].AddExercise(Press, 
																				set4x3, 
																				pW->ExerciseGoalGetAdjustment(Press, 0.7f, 1.0f),
																				0, 
																				calcLinear,
																				filterNothing,
																				false, 0.85f);
			
			break;
			
		case 2: // 3rd week, 1st day
			
			// snatch exercise
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 3;
			reps[3] = 2;
			reps[4] = 2;
			reps[5] = 1;
			reps[6] = 1;
			reps[7] = 1;
			pW->m_workouts[index].AddExercise(Snatch, 
																				8, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.8f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.55f);
			
			// clean and jerk exercise
			reps[0] = 3;
			reps[1] = 3;
			reps[2] = 2;
			reps[3] = 2;
			reps[4] = 2;
			reps[5] = 1;
			pW->m_workouts[index].AddExercise(CleanAndJerk, 
																				6, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.85f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.7f);
			
			reps[0] = 8;
			reps[1] = 4;
			reps[2] = 4;
			reps[3] = 4;
			reps[4] = 4;
			reps[5] = 4;
			pW->m_workouts[index].AddExercise(BackSquat, 
																				6,
																				reps,
																				pW->ExerciseGoalGetAdjustment(BackSquat, 0.85f, 1.0f),
																				0, 
																				calcSine,
																				filterNothing,
																				false, 0.7f);
			
			// overhead squat exercise
			pW->m_workouts[index].AddExercise(Press, 
																				set4x3, 
																				pW->ExerciseGoalGetAdjustment(Press, 0.7f, 1.0f),
																				0, 
																				calcLinear,
																				filterNothing,
																				false, 0.85f);
			
			break;
			
			
		default:
			printf("Olympic Contest 900, week %d, day 1 not defined.\n", week+1);
			break;
	}
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[0]);
	
	/////////////////////////////////////////////////////////////////////
	// 2nd workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Contest - Day 2 (Week %d)", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent);
	
	switch (week) {
		case 0: // 1st week, 2nd day
			
			// Add the exercises to the workout
			pW->m_workouts[index].AddExercise(Snatch, 
																				set4x3, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.7f, 1.0f),
																				0, 
																				calcUnitStep,
																				filterNothing,
																				false, 0.85f);
			
			// Assign the reps for the exercise
			reps[0] = 4;
			reps[1] = 3;
			reps[2] = 3;
			reps[3] = 3;
			reps[4] = 3;
			pW->m_workouts[index].AddExercise(Clean, 
																				5, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.75f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.85f);
			
			
			// Assign the reps for the exercise
			reps[0] = 5;
			reps[1] = 4;
			reps[2] = 2;
			reps[3] = 2;
			reps[4] = 2;
			reps[5] = 2;
			pW->m_workouts[index].AddExercise(JerkRack, 
																				6, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.8f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.75f);
			
			reps[0] = 6;
			pW->m_workouts[index].AddExercise(FrontSquat, 
																				6, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(FrontSquat, 0.8f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.75f);
			
			break;
			
		case 1: // 2nd week, 2nd day
			
			// snatch exercise
			pW->m_workouts[index].AddExercise(SnatchDrop, 
																				set4x5, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.55f, 1.0f),
																				0, 
																				calcFlat,
																				filterNothing,
																				false);
			
			// hang above snatch
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 2;
			reps[3] = 2;
			reps[4] = 2;
			reps[5] = 2;
			pW->m_workouts[index].AddExercise(Snatch, 
																				6, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.85f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.6f);
			
			// power clean
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 4;
			reps[3] = 4;
			reps[4] = 3;
			pW->m_workouts[index].AddExercise(PowerClean, 
																				5, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.65f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.8f);
			
			// front squats
			reps[0] = 6;
			reps[1] = 6;
			reps[2] = 4;
			reps[3] = 3;
			reps[4] = 3;
			reps[5] = 2;
			reps[6] = 2;
			pW->m_workouts[index].AddExercise(FrontSquat, 
																				7, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(FrontSquat, 0.85f, 1.0f),
																				0, calcLinear, filterNothing, 
																				false, 0.82f);
			
			
			break;
			
		default:
			printf("Olympic Contest 900, week %d, day 2 not defined.\n", week+1);
			break;
	}
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[1]);
	
	/////////////////////////////////////////////////////////////////////
	// 3rd workout day
	index = pW->GetWorkoutLastIndex();
	sprintf(des,"Contest - %s (Week %d)","H", week+1);
	pW->m_workouts[index].WorkoutSetDescription(des);
	pW->m_workouts[index].WorkoutSetDate(*pDate);
	//pW->m_workouts[index].WorkoutSetPercentofMax(0.8f);
	pW->m_workouts[index].WorkoutSetPercentofMax(percent);
	
	switch (week) {
		case 0: // 1st week, 3rd day
			
			// Add the exercises to the workout
			// Assign the reps for the exercise
			reps[0] = 6;
			reps[1] = 3;
			reps[2] = 3;
			reps[3] = 3;
//			printf("*************************************************************\n");
//			printf("*************************************************************\n");
			pW->m_workouts[index].AddExercise(PowerClean, 
																				4, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.65f, 1.0f),
																				0, calcUnitStep, filterNothing, 
																				false, 0.85f);
						
//			pW->m_workouts[index].AddExercise(PowerClean, 
//																				4, 
//																				reps, 
//																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.65f, 1.0f),
//																				0, calcUnitStep, filterNothing, 
//																				false, calcUnitStepFactor(0.55f));
//			
//			printf("*************************************************************\n");
//			printf("*************************************************************\n");

			
			// Assign the reps for the exercise
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 3;
			reps[3] = 3;
			reps[4] = 3;
			reps[5] = 2;
			reps[6] = 2;
			reps[7] = 1;
			pW->m_workouts[index].AddExercise(Snatch, 
																				8, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.8f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.75f);
			
			// Assign the reps for the exercise
			reps[0] = 6;
			reps[1] = 4;
			reps[2] = 4;
			reps[3] = 4;
			reps[4] = 4;
			pW->m_workouts[index].AddExercise(BackSquat, 
																				5, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(BackSquat, 0.85f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.8f);
			
			pW->RandomPull(&pW->m_workouts[index], set3x866, false, calcUnitStep);
			
			break;
			
		case 1: // 2nd week, 3rd day
			
			// Add the exercises to the workout
			// Assign the reps for the exercise
			reps[0] = 3;
			reps[1] = 3;
			reps[2] = 3;
			reps[3] = 3;
			reps[4] = 3;
			reps[5] = 2;
			reps[6] = 2;
			pW->m_workouts[index].AddExercise(Snatch, 
																				7, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(Snatch, 0.8f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.65f);
			
			// Assign the reps for the exercise
			reps[0] = 4;
			reps[1] = 4;
			reps[2] = 3;
			reps[3] = 3;
			reps[4] = 3;
			reps[5] = 2;
			reps[6] = 2;
			reps[7] = 1;
			pW->m_workouts[index].AddExercise(JerkRack, 
																				8, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(CleanAndJerk, 0.8f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.75f);
			
			// Assign the reps for the exercise
			reps[0] = 6;
			reps[1] = 4;
			reps[2] = 4;
			reps[3] = 4;
			reps[4] = 4;
			pW->m_workouts[index].AddExercise(BackSquat, 
																				5, 
																				reps, 
																				pW->ExerciseGoalGetAdjustment(BackSquat, 0.85f, 1.0f),
																				0, calcSine, filterNothing, 
																				false, 0.8f);
			
			pW->RandomPress(&pW->m_workouts[index], set3x3, false, calcFlat, 0.5f);

			pW->RandomPull(&pW->m_workouts[index], set3x866, false, calcUnitStep, 0.9f);
			
			break;
			
		default:
			printf("Olympic Contest 900, week %d, day 3 not defined.\n", week+1);
			break;
	}
	
	// increment the workout index, aka we are done with the workout
	pW->IncrementWorkoutCount();
	
	// Increment the current date
	*pDate = SetCalendarDateInterval(*pDate, pDayIncrements[2]);
	
} // OlympicContest9000Proc
